package com.qiantu.phone.signalr;

import android.os.Build;
import android.os.Handler;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.qiantu.api.db.LLControllerDBManager;
import com.qiantu.api.db.LLDeviceDBManager;
import com.qiantu.api.entity.DeviceBean;
import com.qiantu.api.entity.FirmwareUpgradeInfoBean;
import com.qiantu.api.http.HttpBaseData;
import com.qiantu.phone.BuildConfig;
import com.qiantu.phone.app.AppApplication;
import com.qiantu.phone.bean.PushControllerBean;
import com.qiantu.phone.bean.PushDeviceBean;
import com.qiantu.phone.bean.PushHealthRealTimeDataBean;
import com.qiantu.phone.bean.PushInfoBean;
import com.qiantu.phone.event.DeviceItemChangeEvent;
import com.qiantu.phone.event.DeviceDetailChangeEvent;
import com.qiantu.phone.event.DeviceListChangeEvent;
import com.qiantu.phone.event.FirmwareUpgradeInfoEvent;
import com.qiantu.phone.event.RefreshControllerEvent;
import com.qiantu.phone.event.RefreshHealthRealTimeDataEvent;
import com.qiantu.phone.event.RefreshRoomDeviceEvent;
import com.qiantu.phone.utils.PhoneUUIDUtils;
import com.microsoft.signalr.HubConnection;
import com.microsoft.signalr.HubConnectionBuilder;
import com.microsoft.signalr.OnClosedCallback;
import com.microsoft.signalr.TransportEnum;

import org.greenrobot.eventbus.EventBus;
import org.json.JSONObject;

import java.lang.reflect.Type;
import java.util.List;
import java.util.UUID;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.ObservableSource;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;

/**
 * 封装hubConnection
 * 1. 心跳机制
 * 2. 连接状态
 * 3. 监听收到的消息发送出去
 * 4. 开放开始连接，断开连接，发送消息三个方法
 */
public class SignalRManager {

    private String TAG = getClass().getSimpleName();
    private HubConnection hubConnection;

    private long heartDelay = 15 * 1000;
    private long reconnectInterval = 15 * 1000;

    private boolean connectStatus;
    //是否需要重连
    private boolean needReconnect = true;
    private Handler mHandler;
    private ConnectThread mConnectThread;
    //保留当前的房屋编号
    private String mHouseSerialNo;

    public SignalRManager() {
        mHandler = new Handler();
        start();
    }

    private void start(){
        if(mConnectThread != null){
            mConnectThread.isCancel = true;
        }
        mConnectThread = new ConnectThread();
        mConnectThread.start();
    }

    private void setOn() {
        //hubConnection关闭回调
        hubConnection.onClosed(new OnClosedCallback() {
            @Override
            public void invoke(Exception exception) {
                Log.e(TAG, "onClosed");
                connectStatus = false;
                if(needReconnect){
                    mHandler.removeCallbacksAndMessages(null);
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            start();
                        }
                    }, Math.max(heartDelay, reconnectInterval));
                }
            }
        });
        //消息封装，多余一个参数的封装成json，方便后面提取
        //一个参数的，直接放到obj中，在接收的地方根据类型来处理
        hubConnection.on("Send", (result) -> {
            Log.e(TAG, "Send:" + result);
        }, String.class);

        hubConnection.on("received", (result) -> {
            JSONObject jsonObject = null;
            try {
                jsonObject = new JSONObject(result);
                result = jsonObject.toString(2);//最重要的方法，就一行，返回格式化的json字符串，其中的数字4是缩进字符数
                Log.e(TAG, "received:" + result);
                if(TextUtils.isEmpty(result)){
                    return;
                }
                Gson gson = new Gson();
                Type gsonType = new TypeToken<PushInfoBean<JsonObject>>(){}.getType();
                PushInfoBean pushInfo = null;
                try{
                    pushInfo = gson.fromJson(result.replace("&excision&", ""), gsonType);
                }catch (JsonSyntaxException e){
                    gsonType = new TypeToken<PushInfoBean<String>>(){}.getType();
                    pushInfo = gson.fromJson(result.replace("&excision&", ""), gsonType);
                }
                //设备状态改变推送
                if ("LingluRTM.DeviceInfoPush".equals(pushInfo.getNamespace())) {//设备通知信息
                    PushDeviceBean pushDeviceBean = gson.fromJson((JsonElement) pushInfo.getBody().getData(), PushDeviceBean.class);
                    String deviceSerialNo = pushDeviceBean.getDeviceSerialNo();
                    if(deviceSerialNo.equals("")){
                        deviceSerialNo = pushDeviceBean.getDeviceGroupSerialNo();
                    }
                    DeviceBean oldDevice = LLDeviceDBManager.getInstance(AppApplication.getInstance()).getDeviceBySerialNo(deviceSerialNo);
                    if (oldDevice != null) {
                        oldDevice.setStatus(pushDeviceBean.getStateData().getStatus());
                        LLDeviceDBManager.getInstance(AppApplication.getInstance()).put(oldDevice);
                        EventBus.getDefault().post(new DeviceItemChangeEvent(oldDevice.getRoomSerialNo(), oldDevice.getDeviceSerialNo(), oldDevice.getIsFavourite()));
                        EventBus.getDefault().post(new DeviceDetailChangeEvent(oldDevice.getDeviceSerialNo()));
                        EventBus.getDefault().post(new RefreshRoomDeviceEvent());
                    }
                }else if("LingluRTM.TerminalDataUpdatePush".equals(pushInfo.getNamespace())){//主从机添加或者上线通知
                    PushControllerBean pushControllerBean = gson.fromJson((JsonElement) pushInfo.getBody().getData(), PushControllerBean.class);
                    String controllerSerialNo = pushControllerBean.getControllerSerialNo();
                    LLControllerDBManager.getInstance(AppApplication.getInstance())
                            .updateStateType(controllerSerialNo, pushControllerBean.getStateType());
                    EventBus.getDefault().post(new RefreshControllerEvent());
                    Observable.create(new ObservableOnSubscribe<List<String>>() {
                        @Override
                        public void subscribe(ObservableEmitter<List<String>> emitter) throws Exception {
                            List<String> data = LLDeviceDBManager.getInstance(AppApplication.getInstance()).updateStateTypeByControllerSerialNo(controllerSerialNo, pushControllerBean.getStateType());
                            emitter.onNext(data);
                        }
                    }).subscribeOn(Schedulers.computation())
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe(new Consumer<List<String>>() {
                                @Override
                                public void accept(List<String> data) throws Exception {
                                    if (data != null && data.size() > 0) {
                                        //RefreshDeviceEvent主机上下线，会顺带从机上下线短时间多次上线通知，刷新首页出现闪烁，换成DeviceListChangeEvent，只刷新对应上下线设备
//                                        EventBus.getDefault().post(new RefreshDeviceEvent());
                                        EventBus.getDefault().post(new DeviceListChangeEvent(data));
                                    }
                                }
                            }, new Consumer<Throwable>() {
                                @Override
                                public void accept(Throwable throwable) throws Exception {

                                }
                            });
                }else if("LingluRTM.UpgradeInfo".equals(pushInfo.getNamespace())){//固件升级
                    FirmwareUpgradeInfoBean firmwareUpgradeInfoBean = gson.fromJson((JsonElement) pushInfo.getBody().getData(), FirmwareUpgradeInfoBean.class);
                    EventBus.getDefault().post(new FirmwareUpgradeInfoEvent(firmwareUpgradeInfoBean));
                }else if("LingluRTM.HealthRealTimeData".equals(pushInfo.getNamespace())){
                    PushHealthRealTimeDataBean pushHealthRealTimeDataBean = gson.fromJson((String)pushInfo.getBody().getData(), PushHealthRealTimeDataBean.class);
                    EventBus.getDefault().post(new RefreshHealthRealTimeDataEvent(pushHealthRealTimeDataBean));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, String.class);
    }

    /***/
    private class ConnectThread extends Thread {
        public boolean isCancel = false;

        @Override
        public void run() {
            while (!connectStatus && !isCancel) {
                try {
                    //刷新连接状态
                    stopConnect(true);
                    startConnect();
                    Thread.sleep(reconnectInterval);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void heartCheck() {
        new Thread(runnableHeart).start();
    }

    private Runnable runnableHeart = new Runnable() {
        @Override
        public void run() {
            while (connectStatus) {
                long ping = System.currentTimeMillis() / 1000;
                //发送心跳包
                try {
                    send("link", String.valueOf(ping));
                } catch (Exception e) {
                    connectStatus = false;
                }
                //心跳延时
                try {
                    Thread.sleep(heartDelay);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

    /**
     * 开放的三个方法
     */
    public void send(String method, Object message) {
        Log.e(TAG,  method + " " + message);
        try {
            hubConnection.send(method, message);
        } catch (Exception e) {
            connectStatus = false;
        }
    }

    public void stopConnect(boolean needReconnect) {
        connectStatus = false;
        this.needReconnect = needReconnect;
        if(hubConnection != null){
            hubConnection.stop();
        }
        mHandler.removeCallbacksAndMessages(null);
    }

    public void startConnect() {
        Log.e(TAG, "start connect");
        String url = "";
        if(HttpBaseData.getInstance().isLocalService()){
            url = "ws://" + HttpBaseData.getInstance().getLocalServiceWs() + "/hubs/linglu";
        }else{
            url = BuildConfig.WEBSOCKET_URL;
        }
        String clientInfo = Build.BRAND + ",Android" + Build.VERSION.RELEASE;
        hubConnection = HubConnectionBuilder.create(url)
                .withTransport(TransportEnum.WEBSOCKETS)
                .withHeader("Client-System-Info", clientInfo)
                .shouldSkipNegotiate(true)
                .build();
        hubConnection.setServerTimeout(30 * 1000);
        hubConnection.setKeepAliveInterval(heartDelay);
        setOn();
        hubConnection.start().andThen(new ObservableSource<Object>() {
            @Override
            public void subscribe(@NonNull Observer<? super Object> observer) {
                connectStatus = true;
                String accessToken = AppApplication.getInstance().getLoginInfo().getToken();
                String deviceToken = Settings.Secure.getString(AppApplication.getInstance().getContentResolver(), Settings.Secure.ANDROID_ID);
                mHouseSerialNo = AppApplication.getInstance().getCurHouseInfo().getHouseSerialNo();
                String clientID = UUID.randomUUID().toString();
                LinkData linkData = new LinkData();
                linkData.setAccessToken(accessToken);
                linkData.setDeviceToken(deviceToken);
                linkData.setHomeId(mHouseSerialNo);
                linkData.setClientId(clientID);
                linkData.setClientValue("");
                linkData.setPhoneSerialNo(PhoneUUIDUtils.getDeviceUUID(AppApplication.getInstance()));
                linkData.setToken(AppApplication.getInstance().pushDeviceId);
                linkData.setPhoneName( Build.BRAND + " " + Build.MODEL);
                linkData.setPlatform(2);
                linkData.setVersion(Build.VERSION.RELEASE);
                send("link", linkData);
            }
        }).subscribe(new Observer<Object>() {
            @Override
            public void onSubscribe(@NonNull Disposable d) {
                Log.e(TAG, "onSubscribe");
            }

            @Override
            public void onNext(@NonNull Object o) {
                Log.e(TAG, "onNext");
            }

            @Override
            public void onError(@NonNull Throwable e) {
                connectStatus = false;
                Log.e(TAG, e.getMessage());
            }

            @Override
            public void onComplete() {
                Log.e(TAG, "onComplete");
            }
        });
//        heartCheck();
    }

    public String getHouseSerialNo() {
        return mHouseSerialNo;
    }
}
